package com.example.aop.redis.aspect;

import com.example.aop.redis.aop.RepeatSubmit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;
import org.slf4j.Logger;

/**
 * @author: 船长
 * @create: 2024-06-28 21:17
 **/
@Aspect
@Component
public class RepeatSubmitAspect {

    private Logger logger = LoggerFactory.getLogger(RepeatSubmitAspect.class);

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 切入点定义：指明要在什么样的条件下才能被触发
     * @Pointcut: 注解表达式, 通过特定的规则来筛选连接点, 就是Pointcut，选中那几个你想要的方法
     * @annotation：条件：当执行的方法上拥有指定的注解时生效
     */
    @Pointcut("@annotation(com.example.aop.redis.aop.RepeatSubmit)")
    public void pointCut() {
    }

    /**
     * 环绕通知，围绕着方法执行
     * @param pjp
     * @return
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String sessionId = Objects.requireNonNull(RequestContextHolder.getRequestAttributes()).getSessionId();
            assert attributes != null;
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            // 被拦截的方法
            Method method = signature.getMethod();
            // 注解信息
            RepeatSubmit repeatSubmit = method.getAnnotation(RepeatSubmit.class);
            HttpServletRequest request = attributes.getRequest();
            String key = sessionId + "-" + request.getServletPath();
            // 如果缓存中有这个url视为重复提交
            if (redisTemplate.opsForValue().get(key) == null) {
                Object o = pjp.proceed();
                redisTemplate.opsForValue().set(key, request.getRequestURI());
                logger.info("请求的sessionId：" + key + "，请求URI：" + request.getRequestURI());
                redisTemplate.expire(key, repeatSubmit.lockedTime(), repeatSubmit.timeUnit());
                return o;
            } else {
                logger.error("重复提交");
                return "请勿短时间内重复操作";
            }
        } catch (Throwable e) {
            e.printStackTrace();
            logger.error("验证重复提交时出现未知异常!");
            return "验证重复提交时出现未知异常!";
        }
    }
}
